package analisador.lexico;

import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

/*
 * @author Danilo
 * @author Julio
 * @author Lucas
 * @author Willian
 */

public class AnalisadorLexico {
    private Map tabelaPalavrasReservadas = new HashMap();  
    private Map tabelaSimboloReservadas = new HashMap();  
    private BufferedReader in;
    private FileOutputStream out;
    private Character aheadBuffer;
    private String str, strAnterior, strAtual;
    private int countLinha, countColuna;
    static String arquivoEntrada, arquivoSaida;
    private boolean leitura;
    File arquivo; 
    private String mainBuffer = "";
    private boolean reservada = false;
    private String erro = "";
    private boolean encontrouErro = false;
    private boolean isFloat = false;
    public boolean lendoLinha = true;
    private boolean retornouLinha = false;
          
    
    public AnalisadorLexico(){
       String palavras[] = {"algoritmo","fim_algoritmo","declare","literal","inteiro","real","logico","leia","escreva","se","entao","senao","fim_se","caso","seja","fim_caso","tipo","para","verdadeiro","ate","falso","faca","retorne","fim_para","enquanto","fim_enquanto","procedimento","fim_procedimento","funcao","fim_funcao","ou","e","nao","registro","fim_registro","var","constante"};
       String simbolos[] = {":",",","^","(",")","-","%","*","/","+","&","=","<",">",".","[","]","<-","<>","<=",">=",".."};
       countLinha = countColuna = 0;
       for(String p: palavras){
           tabelaPalavrasReservadas.put(p, p);
       }
       for(String s: simbolos){
           tabelaSimboloReservadas.put(s, s);
       }
    }
    
    public AnalisadorLexico(BufferedReader in) {
        this.in = in;
       String palavras[] = {"algoritmo","fim_algoritmo","declare","literal","inteiro","real","logico","leia","escreva","se","entao","senao","fim_se","caso","seja","fim_caso","tipo","para","verdadeiro","ate","falso","faca","retorne","fim_para","enquanto","fim_enquanto","procedimento","fim_procedimento","funcao","fim_funcao","ou","e","nao","registro","fim_registro","var","constante"};
       String simbolos[] = {":",",","^","(",")","-","%","*","/","+","&","=","<",">",".","[","]","<-","<>","<=",">=",".."};
       countLinha = countColuna = 0;
       for(String p: palavras){
           tabelaPalavrasReservadas.put(p, p);
       }
       for(String s: simbolos){
           tabelaSimboloReservadas.put(s, s);
       }
    }
    
    public AnalisadorLexico(BufferedReader in, File out) throws FileNotFoundException {
        this.in = in;
        this.out = new FileOutputStream(out);  
       String palavras[] = {"algoritmo","fim_algoritmo","declare","literal","inteiro","real","logico","leia","escreva","se","entao","senao","fim_se","caso","seja","fim_caso","tipo","para","verdadeiro","ate","falso","faca","retorne","fim_para","enquanto","fim_enquanto","procedimento","fim_procedimento","funcao","fim_funcao","ou","e","nao","registro","fim_registro","var","constante"};
       String simbolos[] = {":",",","^","(",")","-","%","*","/","+","&","=","<",">",".","[","]","<-","<>","<=",">=",".."};
       countLinha = countColuna = 0;
       for(String p: palavras){
           tabelaPalavrasReservadas.put(p, p);
       }
       for(String s: simbolos){
           tabelaSimboloReservadas.put(s, s);
       }
    }

    public void setFileIn(BufferedReader in) {
        this.in = in;
    }

    public void setFileOut(File out) throws FileNotFoundException {
        this.out = new FileOutputStream(out);
    }

    public boolean getLeitura() {
        return leitura;
    }

    public void setLeitura(boolean leitura) {
        this.leitura = leitura;
    }
    
    
    
    
    public int getCountColuna() {
        return countColuna;
    }

    public void setCountColuna(int countColuna) {
        this.countColuna = countColuna;
    }

    public Character getAheadBuffer() {
        return aheadBuffer;
    }

    
    public boolean comentario() throws ExcecaoCompilador{
        lerAFrente();
        if(aheadBuffer.equals('{')){
                //System.out.println("Comentario");
                encontrouErro = false;
                boolean fechouComentario = false;
                while(!fechouComentario && leitura){
                    lerAFrente();
                    fechouComentario = aheadBuffer.equals('}');
                }
                if(!fechouComentario){
                    encontrouErro = true;
                    erro = "Linha " + (countLinha) + ": comentario nao fechado\n";
                    throw new ExcecaoCompilador(erro);
                /*try {
                    out.write(erro.getBytes());
                    //System.out.println("Linha " + (countLinha + 1) + ": comentario nao fechado");
                } catch (IOException ex) {
                    Logger.getLogger(AnalisadorLexico.class.getName()).log(Level.SEVERE, null, ex);
                }*/
                }
                //System.out.println("Sai - Comentario");
                    return true;                       
            }else{
                retornarLeitura();
                return false;
            }
            
    }
    
    public boolean cadeiaLiteral() throws ExcecaoCompilador{
        lerAFrente();
        if(aheadBuffer.equals('"')){
                //System.out.println("cadeia");
                encontrouErro = false;
                int initAsp = countColuna;
                mainBuffer += aheadBuffer;
                if(!lerAFrente()){
                    encontrouErro = true;
                    retornarLeitura();
                }
                while(!aheadBuffer.equals('"') && !encontrouErro){
                    mainBuffer += aheadBuffer;
                    if(!lerAFrente()){
                        encontrouErro = true;
                        retornarLeitura();
                    }
                }
                if(encontrouErro){
                        erro = "Linha " + countLinha + ": \" - simbolo nao identificado\n";
                        countColuna = initAsp;
                        //System.out.println(erro);
                        throw new ExcecaoCompilador(erro);                        
                        /*try {
                           out.write(erro.getBytes());
                        } catch (IOException ex) {
                            Logger.getLogger(AnalisadorLexico.class.getName()).log(Level.SEVERE, null, ex);
                        }*/
                }else{
                    mainBuffer += aheadBuffer;
                    //System.out.println(mainBuffer + " - cadeia_literal");
               /*     try {
                       out.write((mainBuffer + " - cadeia_literal\n").getBytes());
                    } catch (IOException ex) {
                        Logger.getLogger(AnalisadorLexico.class.getName()).log(Level.SEVERE, null, ex);
                    }*/
                }
                //System.out.println("Sai - cadeia");
                return true;
            }else{
                retornarLeitura();
                return false;
            }
    }
    
    public boolean numeroIntReal(){
        lerAFrente();
        if (Character.isDigit(aheadBuffer)){
                //System.out.println("numero");
                mainBuffer += aheadBuffer;
                isFloat = false;
                
                lerAFrente();
                    
                
               while((Character.isDigit(aheadBuffer) && leitura) || (aheadBuffer.equals('.') && !isFloat && leitura)){
                   if(!aheadBuffer.equals('.')){
                            mainBuffer += aheadBuffer;
                            lerAFrente();
                   }
                   
                    if (aheadBuffer.equals('.') && !isFloat){
                        //System.out.println("MMMAAAA OEEEEEE!!");
                        lerAFrente();
                        if (Character.isDigit(aheadBuffer)){
                            isFloat = true;
                            mainBuffer += "." + aheadBuffer;
                            lerAFrente();
                        }else{
                            if(leitura)
                                retornarLeitura();
                            retornarLeitura();
                            isFloat = false;
                            /*try {
                                out.write((mainBuffer + " - numero_inteiro\n").getBytes());
                            } catch (IOException ex) {
                                Logger.getLogger(AnalisadorLexico.class.getName()).log(Level.SEVERE, null, ex);
                            }*/
                            return true;
                        }
                    }else if(aheadBuffer.equals('.')){
                            /*try {
                                out.write((mainBuffer + " - numero_inteiro\n").getBytes());
                            } catch (IOException ex) {
                                Logger.getLogger(AnalisadorLexico.class.getName()).log(Level.SEVERE, null, ex);
                            }*/
                        retornarLeitura();
                        return true;
                    }
                        
                }
               if(!Character.isDigit(aheadBuffer) && leitura)
                   retornarLeitura();
                           /*try {
                               if(isFloat)
                                    out.write((mainBuffer + " - numero_real\n").getBytes());
                               else
                                    out.write((mainBuffer + " - numero_inteiro\n").getBytes());
                            } catch (IOException ex) {
                                Logger.getLogger(AnalisadorLexico.class.getName()).log(Level.SEVERE, null, ex);
                            }*/
               return true;
            }else{
                retornarLeitura();
                return false;
            }
    }
    
    public boolean identificadorPalavraReservada(){
        lerAFrente();
        if (Character.isLetter(aheadBuffer) || aheadBuffer.equals('_')){
                //System.out.println("identificador");
                mainBuffer += aheadBuffer;
                lerAFrente();
                while ((aheadBuffer.equals('_') || Character.isLetterOrDigit(aheadBuffer)) && leitura){
                    mainBuffer += aheadBuffer;
                    lerAFrente();
                }
             
                if(getPalavra(mainBuffer) != null)
                    reservada = true;
                else
                    reservada = false;
                if(leitura)
                    retornarLeitura();
                
                               /*             try {
                                                if(reservada)
                                out.write((mainBuffer + " - " + mainBuffer +"\n").getBytes());
                                                else
                                                    out.write((mainBuffer + " - identificador\n").getBytes());
                            } catch (IOException ex) {
                                Logger.getLogger(AnalisadorLexico.class.getName()).log(Level.SEVERE, null, ex);
                            }*/
                return true;
            }else{
                retornarLeitura();
                return false;
            }
        
    }
    
    public boolean simbolos(){
        lerAFrente();
        if (getSimbolo(aheadBuffer.toString()) != null ){
                //System.out.println("simbolos");
                    mainBuffer += aheadBuffer;
                    lerAFrente();
                if (getSimbolo(mainBuffer + aheadBuffer) != null && leitura)
                    mainBuffer += aheadBuffer;
                else
                    if(leitura)
                        retornarLeitura();
            /*try {
                out.write((mainBuffer + " - " + mainBuffer+"\n").getBytes());
            } catch (IOException ex) {
                Logger.getLogger(AnalisadorLexico.class.getName()).log(Level.SEVERE, null, ex);
            }*/
                return true;
        }else{
                retornarLeitura();
                return false;
         }
    }
    
    public boolean espacos(){
        lerAFrente();
        //System.out.println(aheadBuffer);
        if (aheadBuffer != 0 && !Character.isSpaceChar(aheadBuffer) && !Character.isWhitespace(aheadBuffer)){
                retornarLeitura();
                return false;
        }
        //System.out.println("Espacos");
        return true;
    }
    
    /*public void erro(){
        encontrouErro = true;
        lerAFrente();
        erro = "Linha " + countLinha + ": " + aheadBuffer + " - simbolo nao identificado\n";
        try {
            out.write(erro.getBytes());
        } catch (IOException ex) {
            Logger.getLogger(AnalisadorLexico.class.getName()).log(Level.SEVERE, null, ex);
        }
        System.out.println(erro);
        
    }*/
    
    public void erro() throws ExcecaoCompilador{
        encontrouErro = true;
        lerAFrente();
        erro = "Linha " + countLinha + ": " + aheadBuffer + " - simbolo nao identificado\n";
        throw new ExcecaoCompilador(erro);
    }
    
    
     /*
     * Método para ler um caractere a frente
     */
    public boolean lerAFrente(){
        if(str == null)
            lerLinha();
        if(countColuna >= str.length()){
            aheadBuffer = 0;
            leitura = false;
            //System.out.println("************** PASSEI AKI **************");
            lerLinha();
        }else{
            aheadBuffer = str.charAt(countColuna);
            countColuna++;
            leitura = true;
            //System.out.println("========> " +aheadBuffer+" <========");
            //System.out.println("%%%%%%%%%%%%%% NAO PASSOU NAO %%%%%%%%%%%%%%%");
        }
        return leitura;                
               
    }
    
    /*
     * Método para retornar um caracter lido
     */
    public boolean retornarLeitura(){
        if(countColuna > 0 && leitura){
            countColuna--;
            aheadBuffer = 0;
            retornouLinha = false;
            return true;
        }else if(countColuna == 0){
            str = strAnterior;
            countColuna = str.length() - 1;
            countLinha--;
            aheadBuffer = 0;
            retornouLinha = true;
            return true;
        }
        return false;
    }

    
    
    
    
    
    public Object getPalavra(String value){
        return tabelaPalavrasReservadas.get(value);
    }
    
    public Object getSimbolo(String value){
        return tabelaSimboloReservadas.get(value);
    }

    public int getCountLinha() {
        return countLinha;
    }
    
    
   /*
    * Método para ler uma linha e adicionar em uma variável do tipo string
   */ 
   public boolean lerLinha(){
        try {
            //System.out.println("Passei por aki");
            countColuna = 0;
            if(str != null && str.equals(strAnterior) && retornouLinha){
                str = strAtual;
                countLinha++;
                lendoLinha = true;
                
            }else
                if(in.ready()){
                    strAnterior = str;
                    str = in.readLine();
                    strAtual = str;
                    countLinha++;
                    lendoLinha = true;
                }else
                    lendoLinha = false;
                return lendoLinha;
        } catch (IOException ex) {
            Logger.getLogger(AnalisadorLexico.class.getName()).log(Level.SEVERE, null, ex);
            return false;
        }
   }

    public boolean isEncontrouErro() {
        return encontrouErro;
    }

    public String getErro() {
        return erro;
    }

    public boolean isIsFloat() {
        return isFloat;
    }

    public String getMainBuffer() {
        return mainBuffer;
    }

    public boolean isReservada() {
        return reservada;
    }

    public void setMainBuffer(String mainBuffer) {
        this.mainBuffer = mainBuffer;
    }

    public FileOutputStream getOut() {
        return out;
    }

    public void setOut(String valor) throws IOException {
        out.write(valor.getBytes());
    }
   
    
   
}